﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

namespace Neto.Db.SqlSugar;

/*
 * 单例模式
 https://www.donet5.com/Home/Doc?typeId=2352
 */

/// <summary>
///     sqlsugar 单例模式，可继承，可静态方式使用
/// </summary>
[SuppressSniffer]
public static class SqlSugarHelper //不能是泛型类
{
    //  //用单例模式
    //  public static SqlSugarScope Db = new SqlSugarScope(SqlSugarCore.GetConnectionConfig(),
    //db => db.SqlSugarConfig());

    /// <summary>
    ///     用单例模式
    /// </summary>
    public static SqlSugarScope Db = new(
        SqlSugarDbSetting.GetSqlSugarConnectionConfigs()
        , db => db.InitDbAop()
    );

    /// <summary>
    ///     监听方法
    /// </summary>
    public static ConcurrentDictionary<Type, ConcurrentDictionary<Type, MethodInfo>> ListenerMethodInfos = new();

    /// <summary>
    ///     缓存 sqlsugar 数据库配置 选项
    /// </summary>
    public static SqlSugarDbSettingOptions SqlSugarDbSetting => GetSqlSugarDbSettingOptions();

    /// <summary>
    ///     获取数据库连接配置
    /// </summary>
    /// <returns></returns>
    public static List<ConnectionConfig> GetConnectionConfig()
    {
        return SqlSugarDbSetting.GetSqlSugarConnectionConfigs();
    }

    /// <summary>
    ///     初始化
    /// </summary>
    public static void Init()
    {
        var dics = new Dictionary<Type, string>
        {
            { typeof(ISqlSugarDiffLogListener), "DiffLog" }, //差异日志
            { typeof(ISqlSugarDataChangedListener), "OnChanged" }, //增、删、查 和 改 事件
            { typeof(ISqlSugarAopFilter), "Filter" } //AOP 全局过滤器
        };
        //使用
        //var instance = Activator.CreateInstance(app);
        //method.Invoke(instance, new object[] { dbProvider, diffLogModel });

        {
            foreach (var item in dics)
            {
                var methods = new ConcurrentDictionary<Type, MethodInfo>();

                //item.Key  是type 例如：ISqlSugarDiffLogListener
                //item.value 是 方法名称，例如： "DiffLog";

                var apps = App.EffectiveTypes.Where(u => u.GetInterfaces()
                    .Any(i => i.HasImplementsGeneric(item.Key)));
                foreach (var app in apps)
                {
                    var method = app
                        .GetMethods(BindingFlags.Public | BindingFlags.Instance)
                        .FirstOrDefault(u => u.Name == item.Value
                                             && u.GetParameters().Length > 0);

                    if (method == null) continue;
                    methods.TryAdd(app, method);
                }

                //加入到监听集合中去
                ListenerMethodInfos.TryAdd(item.Key, methods);
            }
        }
    }

    /// <summary>
    ///     根据 Interface 的type 获取监听方法
    /// </summary>
    /// <param name="type">Interface 的type </param>
    /// <returns></returns>
    public static ConcurrentDictionary<Type, MethodInfo> GetMethods(Type type)
    {
        if (ListenerMethodInfos.TryGetValue(type, out var methods))
            return methods;
        return new ConcurrentDictionary<Type, MethodInfo>();
    }

    #region 获取数据库连接配置，AOP

    /// <summary>
    ///     获取 SqlSugarDbSetting Options
    /// </summary>
    /// <returns></returns>
    public static SqlSugarDbSettingOptions GetSqlSugarDbSettingOptions()
    {
        //if (SqlSugarDbSetting != null) return SqlSugarDbSetting;
        var sqlSugarDbSetting = AppX.GetConfig<SqlSugarDbSettingOptions>();
        foreach (var conn in sqlSugarDbSetting.ConnectionConfigs)
        {
            conn.ConfigureExternalServices = EntitySetting(sqlSugarDbSetting, conn);
            conn.MoreSettings = new ConnMoreSettings
            {
                //所有 增、删 、改 会自动调用.RemoveDataCache()
                //意思就是 增、删 、改 都会移除对应表数据的 sqlsugar缓存
                IsAutoRemoveDataCache = true,

                //MySql禁用NVarchar
                DisableNvarchar = conn.DbType == DbType.MySql,
                IsAutoDeleteQueryFilter = sqlSugarDbSetting.IsAutoDeleteQueryFilter, //启用删除查询过滤器
                IsAutoUpdateQueryFilter =
                    sqlSugarDbSetting.IsAutoUpdateQueryFilter, //启用更新查询过滤器 （表达式更新，如果是实体方式更新建议先查询在更新）

                DbMinDate = DateTime.MinValue, //默认最小时间是 1900-01-01 00:00:00.000
                IsWithNoLockQuery = sqlSugarDbSetting.IsWithNoLockQuery, //是否启用全局查询无锁 WITH (NOLOCK)、允许脏读
                IsWithNoLockSubquery = sqlSugarDbSetting.IsWithNoLockSubquery, //是否启用全局子查询无锁 WITH (NOLOCK)、允许脏读

                EnableILike = sqlSugarDbSetting.PgSqlEnableILike, //启用全局查询条件支持ILike,不区分大小写要启用

                #region sqlserver

                SqlServerCodeFirstNvarchar = true, //SqlServer特殊配置：和他库不同一般选用Nvarchar，可以使用这个配置让他和其他数据库区分（其他库是varchar）
                //目前只支持 sqlserver（20240416）
                //SqlServer支持配置 DisableWithNoLockWithTran可以在启用nolock同时让事务查询不nolock
                DisableWithNoLockWithTran =
                    sqlSugarDbSetting.DisableWithNoLockWithTran, //与IsWithNoLockQuery  2个一起用就是事务中不NOLOCK

                #endregion
            };
        }

        //SqlSugarDbSetting = sqlSugarDbSetting;
        return sqlSugarDbSetting;
    }

    /// <summary>
    ///     配置实体
    /// </summary>
    /// <returns></returns>
    public static ConfigureExternalServices EntitySetting(SqlSugarDbSettingOptions sqlSugarDbSetting,
        ConnectionConfig conn)
    {
        ICacheService? sqlSugarCache = sqlSugarDbSetting.EnableDataInfoCacheService ? new SqlSugarMemoryCache() : null;
        return new ConfigureExternalServices
        {
            DataInfoCacheService = sqlSugarCache, //配置我们创建的缓存类，具体用法看标题5
            EntityService = (property, column) =>
            {
                var attributes = property.GetCustomAttributes(true); //get all attributes

                var sugarColumnAttribute = attributes.FirstOrDefault(o => o is SugarColumn);
                if (sugarColumnAttribute != null)
                {
                    //这里是sqlsugar的设置。。暂时没有。。
                }
                else
                {
                    //设置，标记了 Key 属性的字段为主键
                    var keyAttribute = attributes.FirstOrDefault(o => o is KeyAttribute);
                    if (keyAttribute != null) column.IsPrimarykey = true; //有哪些特性可以看 1.2 特性明细
                }

                ////数据库没有进行初始化的时候才设置为自增
                ////必须是ID字段才执行，中间表可能会设置为自增，出现问题
                ////【 因为使用了 IsPrimarykey 所以得放到上面的代码后面 】
                //if (column.DbColumnName != null && column.DbColumnName.ToUpper() == "ID"
                //&& column.IsPrimarykey == true && column.IsIdentity == false
                ////&& sqlSugarDbSetting.EnableInitDatabase == false
                //&& (sqlSugarDbSetting.NoIsIdentityDbTables == null || !sqlSugarDbSetting.NoIsIdentityDbTables.All(a => a.ToLower() != column.DbTableName.ToLower()))
                //)
                //{
                //    //设置自增
                //    column.IsIdentity = sqlSugarDbSetting.EntityIsIdentity;
                //}

                var dbOptions = AppX.GetConfig<SqlSugarDbSettingOptions>();

                #region ColumnAttribute

                {
                    //判断是否贴了 Column 属性
                    if (attributes.Any(o => o is ColumnAttribute))
                    {
                        var attribute = (ColumnAttribute)attributes.First(o => o is ColumnAttribute);

                        if (string.IsNullOrWhiteSpace(column.DataType) &&
                            !string.IsNullOrWhiteSpace(attribute.TypeName))
                            //设置自定义的TypeName
                            //[Column(TypeName = "decimal(18,2)")]
                            //public decimal Payment { get; set; }
                            column.DataType = attribute.TypeName;
                        if (string.IsNullOrWhiteSpace(column.OldDbColumnName) &&
                            !string.IsNullOrWhiteSpace(attribute.Name))
                            //修改列名用，这样不会新增或者删除列
                            column.OldDbColumnName = attribute.Name;
                    }
                }

                #endregion ColumnAttribute

                #region CommentAttribute

                {
                    if (attributes.Any(o => o is CommentAttribute))
                    {
                        var attribute = (CommentAttribute)attributes.First(o => o is CommentAttribute);
                        if (string.IsNullOrWhiteSpace(column.ColumnDescription) &&
                            !string.IsNullOrWhiteSpace(attribute.Comment)) column.ColumnDescription = attribute.Comment;
                    }
                }

                #endregion CommentAttribute

                #region Length

                {
                    if (attributes.Any(o => o is MaxLengthAttribute))
                    {
                        //判断是否设置了字符串的 MaxLength  自动设置
                        var attribute = (MaxLengthAttribute)attributes.First(o => o is MaxLengthAttribute);
                        //设置了 DataType格式的情况 length不要设置
                        if (string.IsNullOrWhiteSpace(column.DataType) && column.Length <= 0 && attribute.Length > 0)
                            column.Length = attribute.Length;
                    }
                }

                #endregion Length

                #region 字符串默认设置

                if (column.PropertyInfo.PropertyType.Name == nameof(String))
                {
                    if (string.IsNullOrWhiteSpace(column.DataType) && column is { Length: <= 0, IsJson: false })
                        //没有设置 DataType，且字符串长度也没设置的情况下，默认使用大文本
                        column.DataType = StaticConfig.CodeFirst_BigString;

                    //默认都把字符串设置为可空,设置为false的话，根据实际配置来
                    if (dbOptions.StringIsNullable) column.IsNullable = dbOptions.StringIsNullable;
                }

                #endregion 字符串默认设置

                #region 是否设置为自增

                //{
                //    //主键必须是是 int、long类型
                //    //主键
                //    if ((column.PropertyInfo.PropertyType.Name == nameof(System.Int32)
                //    || column.PropertyInfo.PropertyType.Name == nameof(System.Int64))
                //    && column.IsPrimarykey == false
                //    && dbOptions.EntityIsIdentity
                //    && !attributes.Any(o => o is SugarNoIdentityAttribute)//没有贴不自增的标签
                //    )
                //    {
                //        var tablenames = new List<string>() { column.DbTableName };
                //        if (dbOptions.EnableDbColumnNameToUnderLine)
                //        {
                //            var newtablename = UtilMethods.ToUnderLine(column.DbTableName);
                //            tablenames.Add(newtablename);
                //        }
                //        //是否不自增，默认false
                //        var isNoIdentity = false;
                //        foreach (var tablename in dbOptions?.NoIsIdentityDbTables ?? new List<string>())
                //        {
                //            if (tablenames.Any(o => o.ToLower() == tablename.ToLower()))
                //            {
                //                isNoIdentity = true;
                //                break;
                //            }
                //        }
                //        //没有被排除，可以设置为自增
                //        if (isNoIdentity == false)
                //        {
                //            column.IsIdentity = true;
                //        }
                //    }
                //}

                #endregion 是否设置为自增

                //else if (string.IsNullOrWhiteSpace(column.DataType))
                //{
                //    //string  大文本
                //    column.DataType = StaticConfig.CodeFirst_BigString;
                //}

                //// int?  decimal?这种 isnullable=true 不支持string(下面.NET 7支持)
                //if (column.IsPrimarykey == false && column.IsNullable == false && property.PropertyType.IsGenericType &&
                //property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                //{
                //    column.IsNullable = true;
                //}
                /***高版C#写法***/
                //int?  decimal?这种 isnullable=true  支持string?和string
                if (column is { IsPrimarykey: false, IsNullable: false } && new NullabilityInfoContext()
                        .Create(property).WriteState is NullabilityState.Nullable)
                    column.IsNullable = true;

                //如果是mysql的情况下，定义了 [SugarColumn(ColumnDataType = "varchar(max)")]，
                //此设置只支持sqlserver（max mysql不支持）兼容mysql 则修改为  longtext
                if (conn.DbType == DbType.MySql && column.DataType == "varchar(max)")
                    column.DataType = "longtext";

                #region 如果是pgsql,字段类型是 DateTimeOffset，且未设置数据类型

                else if (conn.DbType == DbType.PostgreSQL && string.IsNullOrWhiteSpace(column.DataType))
                {
                    // 假设 propInfo 是 PropertyInfo 实例
                    var propertyType = column.PropertyInfo.PropertyType;

                    // 检查是否为可空类型
                    if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        // 获取可空类型的泛型参数
                        var underlyingType = Nullable.GetUnderlyingType(propertyType);

                        // 检查是否为 DateTimeOffset 类型
                        if (underlyingType == typeof(DateTimeOffset))
                            // 在这里处理 DateTimeOffset 类型的逻辑
                            // underlyingType 现在包含 DateTimeOffset 类型
                            column.DataType = "timestamptz"; // "timestamp with time zone";
                    }
                    else if (propertyType.Name == "DateTimeOffset")
                        column.DataType = "timestamptz"; // "timestamp with time zone";
                }

                #endregion 如果是pgsql,字段类型是 DateTimeOffset，且未设置数据类型

                //ToUnderLine驼峰转下划线方法
                if (dbOptions.EnableDbColumnNameToUnderLine)
                {
                    column.DbColumnName = UtilMethods.ToUnderLine(column.DbColumnName);
                    column.OldDbColumnName = UtilMethods.ToUnderLine(column.OldDbColumnName);
                }
            },
            //这个是设置数据库表的名称的，例如，实体叫“User” 数据库叫“Sys_User”
            //解析了 EF的 Table 属性
            EntityNameService = (type, entity) =>
            {
                var attributes = type.GetCustomAttributes(true);
                if (attributes.Any(it => it is TableAttribute))
                {
                    var attr = attributes.FirstOrDefault(it => it is TableAttribute);
                    if (attr != null)
                    {
                        var tableAttribute = (TableAttribute)attr;
                        entity.DbTableName = tableAttribute.Name;
                    }

                    //entity.DbTableName = (attributes.First(it => it is TableAttribute) as TableAttribute).Name;
                }

                var dbOptions = AppX.GetConfig<SqlSugarDbSettingOptions>();
                //ToUnderLine驼峰转下划线方法
                if (dbOptions.EnableDbTableNameToUnderLine) entity.DbTableName = ToUnderLine(entity.DbTableName);

                //默认创建的索引
                CreateIndex(type, entity, "CreationTime");
                CreateIndex(type, entity, "UpdateTime");
                return;

                void CreateIndex(Type type2, EntityInfo entity2, string columnName)
                {
                    //if(type.Name=="SysLog")
                    //{

                    //}
                    //NoAutoCreateCreationTimeAttribute
                    var properties = type2.GetCustomAttributes();
                    if (properties.Any(o => o is NoAutoCreateIndexAttribute))
                    {
                        //跳过，贴了跳过
                    }
                    else
                    {
                        //var isCreationTime = entity.Columns.Any(o => o.DbColumnName == "CreationTime");
                        var isCreationTime = type2.GetProperties().Any(o => o.Name == columnName);
                        var isCreationTimeIndex =
                            entity2.Indexs?.Any(o => o.IndexFields?.Keys?.Contains(columnName) ?? false) ?? false;
                        //这个实体包含创建时间字段，但是没有创建时间索引
                        if (isCreationTime && !isCreationTimeIndex)
                        {
                            entity2.Indexs ??= [];
                            var indexname = $"Index_{entity2.DbTableName}_{columnName}";
                            //需要处理分表的情况：例如：sys_log_{year}{month}{day}_CreationTime
                            indexname = indexname.Replace("{year}", $"{DateTimeOffset.Now:yyyy}");
                            indexname = indexname.Replace("{month}", $"{DateTimeOffset.Now:MM}");
                            indexname = indexname.Replace("{day}", "01");
                            //indexname = indexname.Replace("{Week}", $"01");//按周
                            //indexname = indexname.Replace("{Month_6}", $"01");//按半年
                            //indexname = indexname.Replace("{Season}", $"01");//按季度

                            entity2.Indexs.Add(new SugarIndexAttribute(indexname, columnName, OrderByType.Desc));
                        }
                    }
                }
            }
        };
    }

    /// <summary>
    /// </summary>
    /// <param name="str"></param>
    /// <param name="isToUpper"></param>
    /// <param name="isSkipUnderLine">是否跳过有下划线的</param>
    /// <returns></returns>
    public static string ToUnderLine(string str, bool isToUpper = false, bool isSkipUnderLine = false)
    {
        if ((str?.Contains("_") ?? true) && isSkipUnderLine) return str;
        var newTableName = "";
        if (isToUpper)
            newTableName = string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x : x.ToString()))
                .ToUpper();

        newTableName = string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x : x.ToString())).ToLower();

        newTableName = newTableName.Replace("__", "_");
        return newTableName;
    }

    #endregion 获取数据库连接配置，AOP

    #region 缓存

    // /// <summary>
    // /// 删除SqlSugar 缓存
    // /// </summary>
    // /// <typeparam name="TEntity">数据库实体名</typeparam>
    // public static void RemoveCacheByContainEntityName<TEntity>()
    // {
    //     Db.RemoveCacheByContainEntityName<TEntity>();
    // }

    // /// <summary>
    // /// 删除包含指定支付查的 SqlSugar 缓存
    // /// </summary>
    // /// <param name="likeString">相似字符串</param>
    // public static void RemoveCacheByContainStr(string likeString)
    // {
    //     Db.RemoveCacheByContainStr(likeString);
    // }

    #region 刷新表缓存异步

    /// <summary>
    ///     刷新表缓存
    /// </summary>
    /// <returns></returns>
    public static async Task<List<TEntity>> RefreshCacheByTableAsync<TEntity>()
    {
        var entities = await Db.RefreshTableCacheByEntityAsync<TEntity>();
        return entities;
    }

    /// <summary>
    ///     刷新表缓存
    /// </summary>
    /// <param name="entityName">实体名称</param>
    /// <returns></returns>
    public static async Task<List<object>> RefreshCacheByTableAsync(string entityName)
    {
        var entities = await Db.RefreshTableCacheByEntityNameAsync(entityName);
        return entities;
    }

    #endregion 刷新表缓存异步

    #region 刷新表缓存同步

    /// <summary>
    ///     刷新表缓存
    /// </summary>
    /// <returns></returns>
    public static List<TEntity> RefreshCacheByTable<TEntity>()
    {
        var entities = Db.RefreshTableCacheByEntity<TEntity>();
        return entities;
    }

    /// <summary>
    ///     刷新表缓存
    /// </summary>
    /// <param name="entityName">实体名称</param>
    /// <returns></returns>
    public static List<object> RefreshCacheByTable(string entityName)
    {
        var entities = Db.RefreshTableCacheByEntityName(entityName);
        return entities;
    }

    #endregion 刷新表缓存同步

    #endregion 缓存

    #region 数据库初始化/数据种子

    /// <summary>
    ///     初始化数据库
    /// </summary>
    public static void InitDb()
    {
        var sqlSugarDbSetting = SqlSugarDbSetting;

        //判断是否是初始化且启用了初始化数据库
        if (sqlSugarDbSetting.EnableInitDatabase == false) return;

        InitTables();
    }

    /// <summary>
    ///     创建数据库
    /// </summary>
    /// <param name="config">数据库配置</param>
    /// <returns></returns>
    public static string CreateDb(SqlSugarDbTableConfig config)
    {
        var sqlSugarDbSetting = SqlSugarDbSetting;

        var resultMsg = "";
        try
        {
            if (string.IsNullOrWhiteSpace(config.DatabaseDirectory) &&
                !string.IsNullOrWhiteSpace(sqlSugarDbSetting.DatabaseDirectory))
                config.DatabaseDirectory = sqlSugarDbSetting.DatabaseDirectory;

            //创建数据库
            //注意 ：Oracle和个别国产库需不支持该方法，需要手动建库
            var db = Db;
            foreach (var conn in sqlSugarDbSetting.ConnectionConfigs.Where(conn =>
                         conn.DbType != DbType.Oracle && !string.IsNullOrWhiteSpace(config.DatabaseName)))
                db.DbMaintenance.CreateDatabase(config.DatabaseName, config.DatabaseDirectory);

            resultMsg = "创建成功";
        }
        catch (Exception ex)
        {
            LogX.Error(ex, "SqlSugar\\创建数据库");

            resultMsg = ex.Message;
        }

        return resultMsg;
    }

    /// <summary>
    ///     创建表
    /// </summary>
    /// <param name="config">数据库配置</param>
    public static void InitTables(SqlSugarDbTableConfig? config = null)
    {
        try
        {
            var sqlSugarDbSetting = SqlSugarDbSetting;
            //创建数据库
            if (config != null)
                CreateDb(config);
            else
                //创建所有连接的数据库
                foreach (var item in sqlSugarDbSetting.ConnectionConfigs)
                    CreateDb(new SqlSugarDbTableConfig
                    {
                        DbConn = item.ConnectionString,
                        DbType = item.DbType,
                        DatabaseName = item.Database,
                        DatabaseDirectory = sqlSugarDbSetting.DatabaseDirectory
                    });

            //获取所有实体的 Type
            var entityTypes = GetAllEntitiesTypes();
            ////创建表、迁移表
            //SqlSugarDbContext.Db.ScopedContext.CodeFirst.InitTables(alltypes.ToArray());//根据types创建表

            var db = Db;
            foreach (var entityType in entityTypes)
                try
                {
                    //判断是否启用了租户，没有启用，则不生成租户配置表
                    if (sqlSugarDbSetting.EnableTanant == false && entityType.Name == "SysTenantDbConfig") continue;
                    //是否贴了 租户属性，如果贴了取出贴了的 ConfigId
                    var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
                    var configId = tAtt?.configId.ToString() ?? "";
                    if (configId == null || string.IsNullOrWhiteSpace(configId))
                        configId = sqlSugarDbSetting.ConnectionConfigs.FirstOrDefault()?.ConfigId?.ToString();
                    if (string.IsNullOrWhiteSpace(configId)) configId = SqlSugarConst.ConfigId;
                    //var provider = _db.GetConnectionScope(tAtt == null ? SqlSugarConst.ConfigId : tAtt.configId);
                    var provider = db.GetConnectionScope(configId);
                    //provider.CodeFirst.SetStringDefaultLength(0);//设置字符串默认长度
                    provider.CodeFirst
                        .SplitTables()
                        .InitTables(entityType);
                }
                catch (Exception ex)
                {
                    LogX.Error($"表：{entityType.FullName}\r\n{ex.ToStringEx()}", "SqlSugar\\初始化数据库和表");
                }
        }
        catch (Exception ex)
        {
            LogX.Error(ex, "SqlSugar\\初始化数据库和表");
        }
    }

    /// <summary>
    ///     获取DB更加sqlsugar数据库配置
    /// </summary>
    /// <param name="config">数据库配置</param>
    /// <returns></returns>
    public static SqlSugarClient GetDbBySqlSugarDbTableConfig(SqlSugarDbTableConfig config)
    {
        if (config != null && !string.IsNullOrWhiteSpace(config.DbConn))
        {
            var conn = new ConnectionConfig
            {
                DbType = config.DbType, ConnectionString = config.DbConn, IsAutoCloseConnection = true
            };
            var db = new SqlSugarClient(conn);
            db.SetAop();

            return db;
        }

        // throw new Exception("数据库字符串连接配置错误");
        return Db.ToClient();
    }

    ///// <summary>
    ///// 初始化数据种子委托方法，默认内置，可自定义
    ///// </summary>
    //public static Func<ISqlSugarClient, bool, bool> InitDataSeedFunc { get; set; }

    /// <summary>
    ///     初始化数据种子委托方法扩展，执行完数据种子方法后调用，处理一些特殊扩展
    /// </summary>
    public static Func<ISqlSugarClient, bool> InitDataSeedExFunc { get; set; }

    /// <summary>
    ///     初始化数据种子
    /// </summary>
    /// <returns></returns>
    public static bool InitDataSeed()
    {
        var sqlSugarDbSetting = SqlSugarDbSetting;
        //判断是否是初始化且启用了初始化数据库
        if (sqlSugarDbSetting.EnableInitDatabase == false) return false;

        return UpdateDataSeed();
    }

    /// <summary>
    ///     更新数据种子
    /// </summary>
    public static bool UpdateDataSeed(SqlSugarDbTableConfig config = null)
    {
        if (config != null)
        {
            ISqlSugarClient db = GetDbBySqlSugarDbTableConfig(config);

            return UpdateDataSeed(db.ToScope());
        }

        return UpdateDataSeed(Db);
    }

    /// <summary>
    ///     更新数据种子
    /// </summary>
    public static bool UpdateDataSeed(SqlSugarScope db)
    {
        try
        {
            var sqlSugarDbSetting = SqlSugarDbSetting;

            // 获取所有实体种子数据
            var seedDataTypes = App.EffectiveTypes.Where(u =>
                !u.IsInterface && u is { IsAbstract: false, IsClass: true }
                               && u.GetInterfaces().Any(i =>
                                   i.HasImplementsGeneric(
                                       typeof(ISqlSugarEntitySeedData<>))));
            if (!seedDataTypes.Any()) return false;

            //循环处理数据种子
            foreach (var seedType in seedDataTypes)
                try
                {
                    var instance = Activator.CreateInstance(seedType);
                    //获取数据种子的方法
                    var hasDataMethod = seedType.GetMethod("HasData", 0, new Type[] { });
                    //获取数据种子的数据
                    var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
                    if (seedData == null) continue;

                    var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
                    var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
                    //配置ID
                    var configId = tAtt?.configId.ToString() ?? "";
                    if (configId == null || string.IsNullOrWhiteSpace(configId))
                        configId = sqlSugarDbSetting.ConnectionConfigs.FirstOrDefault()?.ConfigId?.ToString();
                    if (string.IsNullOrWhiteSpace(configId)) configId = SqlSugarConst.ConfigId;

                    //获取指定配置的连接
                    var provider = db.GetConnectionScope(configId);

                    var entityInfo = provider.EntityMaintenance.GetEntityInfo(entityType);

                    // var primarykeys = string.Join(',', entityInfo.Columns.Where(o => o.IsPrimarykey).Select(o => o.DbColumnName));
                    //var aa = provider.EntityMaintenance.GetDbColumnName("ExtensionData", entityType);
                    //手动构造 Where构造模式
                    var conModels = new List<IConditionalModel>();

                    foreach (var item in seedData)
                    {
                        //清空
                        conModels.Clear();
                        //获取数据的dictionary
                        var dics = item.GetDictionary();
                        foreach (var entityColumnInfo in entityInfo.Columns.Where(o => o.IsPrimarykey))
                        {
                            var state = dics.TryGetValue(entityColumnInfo.PropertyName, out var value);
                            if (state && !string.IsNullOrWhiteSpace(value))
                                conModels.Add(new ConditionalModel
                                {
                                    FieldName = entityColumnInfo.DbColumnName,
                                    CSharpTypeName = entityColumnInfo.PropertyInfo.PropertyType.Name,
                                    ConditionalType = ConditionalType.Equal,
                                    FieldValue = value
                                });
                        }

                        //有条件才进行，否则可能会出错
                        if (conModels.Any())
                        {
                            //数据库是否存在
                            var isExist = db.Queryable<object>().AS(entityInfo.DbTableName).Where(conModels).Any();
                            if (isExist)
                            {
                                //var result2 = db.UpdateableByObject(item).ExecuteCommand();
                            }
                            else
                                db.InsertableByObject(item).ExecuteCommand();
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogX.Error($"表：{seedType.FullName}\r\n{ex.ToStringEx()}", "SqlSugar\\初始化数据种子");
                }

            // 初始化数据种子委托方法扩展，执行完数据种子方法后调用，处理一些特殊扩展
            if (InitDataSeedExFunc != null) return InitDataSeedExFunc(db);

            return true;
        }
        catch (Exception ex)
        {
            LogX.Error(ex, "SqlSugar\\初始化数据种子");
            return false;
        }
    }

    /// <summary>
    ///     初始化数据种子
    /// </summary>
    private static bool InitDataSeed_old(bool isInit = false)
    {
        try
        {
            var db = Db;

            var sqlSugarDbSetting = SqlSugarDbSetting;
            //判断是否是初始化且启用了初始化数据库
            if (sqlSugarDbSetting.EnableInitDatabase == false && isInit) return false;

            //判断是否是初始化且启用了初始化数据种子
            if (sqlSugarDbSetting.EnableInitDataSeed == false && isInit) return false;

            // 获取所有实体种子数据
            var seedDataTypes = App.EffectiveTypes.Where(u =>
                !u.IsInterface && u is { IsAbstract: false, IsClass: true }
                               && u.GetInterfaces().Any(i =>
                                   i.HasImplementsGeneric(
                                       typeof(ISqlSugarEntitySeedData<>))));
            if (!seedDataTypes.Any()) return false;

            //循环处理数据种子
            foreach (var seedType in seedDataTypes)
                try
                {
                    var instance = Activator.CreateInstance(seedType);
                    //获取数据种子的方法
                    var hasDataMethod = seedType.GetMethod("HasData", 0, new Type[] { });
                    //获取数据种子的数据
                    var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
                    if (seedData == null) continue;

                    var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
                    var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
                    //配置ID
                    var configId = tAtt?.configId.ToString() ?? "";
                    if (configId == null || string.IsNullOrWhiteSpace(configId))
                        configId = sqlSugarDbSetting.ConnectionConfigs.FirstOrDefault()?.ConfigId?.ToString();
                    if (string.IsNullOrWhiteSpace(configId)) configId = SqlSugarConst.ConfigId;

                    //var provider = db.GetConnectionScope(tAtt == null ? SqlSugarConst.ConfigId : tAtt?.configId);
                    //获取指定配置的连接
                    var provider = db.GetConnectionScope(configId);

                    var seedDataTable = seedData.ToList().ToDataTable();
                    seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName;
                    if (seedDataTable.Columns.Contains(SqlSugarConst.PrimaryKey))
                    {
                        var storage = provider.Storageable(seedDataTable).WhereColumns(SqlSugarConst.PrimaryKey)
                            .ToStorage();

                        // 如果添加一条种子数，sqlsugar 默认以 @param 的方式赋值，如果 PropertyType 为空，则默认数据类型为字符串。插入 pgsql 时候会报错，所以要忽略为空的值添加
                        var isSingle = ((InsertableProvider<Dictionary<string, object>>)storage.AsInsertable).IsSingle;
                        if (isSingle)
                            //var sql = storage.AsInsertable.IgnoreColumns("Id", "UpdateTime", "UpdateUserId", "CreatorUserId").ToSqlString();
                            storage.AsInsertable.ExecuteCommand();
                        else
                        {
                            var a = storage.AsInsertable.ToSqlString();
                            storage.AsInsertable.ExecuteCommand();
                        }

                        //_ = ((InsertableProvider<Dictionary<string, object>>)storage.AsInsertable).IsSingle ?
                        //    storage.AsInsertable.IgnoreColumns("UpdateTime", "UpdateUserId", "CreatorUserId", "Id").ExecuteCommand() :
                        //    storage.AsInsertable.IgnoreColumns("Id").ExecuteCommand();
                        storage.AsUpdateable.ExecuteCommand();
                    }
                    else // 没有主键或者不是预定义的主键(没主键有重复的可能)
                    {
                        var storage = provider.Storageable(seedDataTable).ToStorage();
                        storage.AsInsertable.ExecuteCommand();
                    }
                }
                catch (Exception ex)
                {
                    LogX.Error($"表：{seedType.FullName}\r\n{ex.ToStringEx()}", "SqlSugar\\初始化数据种子");
                }

            return true;
        }
        catch (Exception ex)
        {
            LogX.Error(ex, "SqlSugar\\初始化数据种子");
            return false;
        }
    }

    /// <summary>
    ///     数据库表 结构对比
    /// </summary>
    /// <returns>返回表结构对比结果</returns>
    public static string DifferenceTables(string dbType, string connStr)
    {
        var dt = dbType.ToEnum<DbType>();
        return DifferenceTables(new SqlSugarDbTableConfig { DbType = dt, DbConn = connStr });
    }

    /// <summary>
    ///     数据库表 结构对比
    /// </summary>
    /// <returns>返回表结构对比结果</returns>
    public static string DifferenceTables(SqlSugarDbTableConfig config)
    {
        if (string.IsNullOrWhiteSpace(config.DbConn))
            //对比当前数据库
            return DifferenceTables();
        var db = GetDbBySqlSugarDbTableConfig(config);
        return DifferenceTables(db);
    }

    /// <summary>
    ///     数据库表 结构对比
    /// </summary>
    /// <returns>返回表结构对比结果</returns>
    public static string DifferenceTables(ConnectionConfig conn)
    {
        conn.IsAutoCloseConnection = true;
        var db = new SqlSugarClient(conn);
        db.SetAop(); //设置AOP
        return db.DifferenceTables();
    }

    /// <summary>
    ///     数据库表 结构对比
    /// </summary>
    /// <returns>返回表结构对比结果</returns>
    public static string DifferenceTables()
    {
        return Db.DifferenceTables();
    }

    /// <summary>
    ///     数据库表 结构对比
    /// </summary>
    /// <returns>返回表结构对比结果</returns>
    public static string DifferenceTables(this ISqlSugarClient db)
    {
        db ??= Db;
        //获取所有实体的 Type
        var alltypes = GetAllEntitiesTypes();
        //对比表
        var diffString = db.CodeFirst.GetDifferenceTables(alltypes.ToArray()).ToDiffString();
        //返回表的对比结果
        return diffString;
    }

    /// <summary>
    ///     获取所有实体的 Type
    /// </summary>
    /// <param name="isCompatibleEf">是否兼容EF</param>
    /// <returns></returns>
    public static Type[] GetAllEntitiesTypes(bool isCompatibleEf = true)
    {
        if (isCompatibleEf)
        {
            var types = App.EffectiveTypes.Where(u =>
                !u.IsInterface
                && u is { IsAbstract: false, IsClass: true }
                && (u.IsDefined(typeof(SugarTable), false) || u.IsDefined(typeof(TableAttribute), false))
                && !u.IsDefined(typeof(NotTableAttribute), false)
            ).ToArray();

            return types;
        }
        else
        {
            var types = App.EffectiveTypes.Where(u =>
                !u.IsInterface
                && u is { IsAbstract: false, IsClass: true }
                && u.IsDefined(typeof(SugarTable), false)
                && !u.IsDefined(typeof(NotTableAttribute), false)
            ).ToArray();

            return types;
        }
    }

    #endregion 数据库初始化/数据种子

    #region Comm

    /// <summary>
    ///     SqlSugar获取实体信息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="db"></param>
    /// <returns></returns>
    public static EntityInfo GetEntityInfoByAllDb<T>(ISqlSugarClient db = null)
    {
        return GetEntityInfoByAllDb(typeof(T), db);
    }

    /// <summary>
    ///     SqlSugar获取实体信息
    /// </summary>
    /// <param name="type"></param>
    /// <param name="db"></param>
    /// <returns></returns>
    public static EntityInfo GetEntityInfoByAllDb(Type type, ISqlSugarClient db = null)
    {
        foreach (var conn in GetConnectionConfig())
        {
            var db2 = db.ToTenant((string)conn.ConfigId);
            var entityInfo = db2.EntityMaintenance.GetEntityInfo(type);
            if (entityInfo != null) return entityInfo;
        }

        return null;
    }

    #endregion Comm
}